package com.biconn.bigdata.es.repository;
/**
 * Created by Administrator on 2018/8/4.
 */

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.biconn.bigdata.common.util.JsonUtil;
import com.biconn.bigdata.common.util.StringTools;
import com.biconn.bigdata.common.util.UIDGenerator;
import com.biconn.bigdata.es.client.config.AnalyzedEnum;
import com.biconn.bigdata.es.client.config.BeanPropertyResolve;
import com.biconn.bigdata.es.client.config.Dictionary;
import com.biconn.bigdata.es.client.config.ElasticsearchMapping;
import com.biconn.bigdata.es.client.config.StoreEnum;
import com.biconn.bigdata.es.client.config.TypeEnum;
import com.biconn.bigdata.es.client.config.annontation.IndexAnnontation;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 基类的实现类
 * botu
 * com.bicon.botu.es
 *
 * @author 123774135@qq.com
 * @create 2018-08-04 18:31
 **/
@Component
public class DefaultElasticsearchRepository<T> implements BaseElasticsearchRepository<T> {

    @Autowired
    private ElasticsearchClientRepository elasticsearchClientRepository;

    private  UIDGenerator uuidgenerator = new UIDGenerator();
    /**
     * From index to start the search from. Defaults to <tt>0</tt>.
     */
    private static int default_page = 0;
    /**
     * The number of search hits to return. Defaults to <tt>10</tt>.
     */
    private static int default_size = 10;
    
    private static String elastic_object_id_key = "id";

    @SuppressWarnings("unchecked")
	@Override
    public int save(Object o) {
        try {
            IndexAnnontation indexAnnontation = o.getClass().getAnnotation(IndexAnnontation.class);
            TransportClient client = elasticsearchClientRepository.getObject();
            String json = o.toString();//o必须tostring，里面的实现必须用json去实现
          //注解的属性和javabean的属性有可能不一样的场景，//建立 注解和字段对于关系
            Map<String,String> field_ann_map = BeanPropertyResolve.builderFileAndAnnontationMpper(o);
            Map<String,String> map = (Map<String,String>)JsonUtil.jsonToObject(json,Map.class);
            Map<String,String> transform = Maps.transformValues(field_ann_map, new  Function<String,String>(){

				@Override
				public String apply(String input) {
					 String value = map.get(input);
					return value;
				}
            	
            });
            IndexResponse response = client.prepareIndex(indexAnnontation.indexName(), indexAnnontation.typeName(), uuidgenerator.generate().toString()).setSource(transform).get();
            int status = response.status().getStatus();
            return status == 201 ? 200 : 500;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
    }

    @Override
    public boolean delet(String indexName) {
    	boolean result = false;
    	try {
    		TransportClient client = elasticsearchClientRepository.getObject();
			DeleteByQueryAction.INSTANCE.newRequestBuilder(client)
	         .source(indexName)
	         .filter(QueryBuilders.matchAllQuery())
	         .get();
			return true;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
       return result;
    }

    @Override
    public int saveIndex(Object o) {
        List<ElasticsearchMapping> mappingList = BeanPropertyResolve.builderElasticsearchMapping(o);
        IndexAnnontation indexAnnontation = o.getClass().getAnnotation(IndexAnnontation.class);
        try {
            TransportClient client = elasticsearchClientRepository.getObject();
            //先创建空索引库
            HashMap<String, Object> settings_map = new HashMap<String, Object>(2);
    		settings_map.put(Dictionary.NUMBER_OF_SHARDS.name().toLowerCase(), indexAnnontation.number_of_shards());
    		settings_map.put(Dictionary.NUMBER_OF_REPLICAS.name().toLowerCase(), indexAnnontation.number_of_replicas());

           //client.admin().indices().prepareCreate(indexAnnontation.indexName()).execute().actionGet();
            //put mapping
            XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(indexAnnontation.typeName()).startObject("properties");
            if(!mappingList.isEmpty()){
                for(ElasticsearchMapping elasticsearchMapping : mappingList){
                    //String name =  elasticsearchMapping.getName();
                    String name = elasticsearchMapping.getName();
                    TypeEnum type = elasticsearchMapping.getType();
                    StoreEnum store = elasticsearchMapping.getStore();
                    AnalyzedEnum index = elasticsearchMapping.getIndex();
                    String searchAnalyzer = elasticsearchMapping.getSearchAnalyzer();//搜索用哪种分词器
                    String analyzer = elasticsearchMapping.getAnalyzer();//分词用哪种分词器
                    AnalyzedEnum item = elasticsearchMapping.getSimilarity();
                    mapping.startObject(name).field("type",type.name().toLowerCase()).field("store",store.name().toLowerCase()).field("index",index.name().toLowerCase()).field("similarity", item.name());
                    if(!StringTools.isBlank(searchAnalyzer)){
                        mapping =  mapping.field("search_analyzer",searchAnalyzer);
                    }
                    if(!StringTools.isBlank(analyzer)){
                        mapping = mapping.field("analyzer",analyzer);
                    }
                    mapping.endObject();
                }

            }
            mapping.endObject().endObject().endObject();
            CreateIndexRequestBuilder prepareCreate = client.admin().indices().prepareCreate(indexAnnontation.indexName());
            CreateIndexResponse indexResponse =  prepareCreate.setSettings(settings_map).addMapping(indexAnnontation.typeName(), mapping).execute().actionGet();
            return  indexResponse.isAcknowledged()?200:500;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
    }

	@Override
	public int deleteIndex(Object t) {
		 try {
			IndexAnnontation indexAnnontation = t.getClass().getAnnotation(IndexAnnontation.class);
			TransportClient client = elasticsearchClientRepository.getObject();
			DeleteIndexResponse dResponse =  client.admin().indices().prepareDelete(indexAnnontation.indexName()).execute().actionGet();
			return  dResponse.isAcknowledged()?200:500;
		} catch (Exception e) {
			e.printStackTrace();
		}
		 return 500;
	}

	@Override
	public int updateIndex(Object t) {
		
		 return 500;
	}

	@Override
	public void findIndex(String indexname) {
		try {
			GetIndexRequest getIndexRequest = new GetIndexRequest();
			getIndexRequest.indices(Strings.toStringArray((Collection<String>) Lists.newArrayList(indexname)));
			TransportClient client = elasticsearchClientRepository.getObject();
			GetIndexResponse indexResponse =	client.admin().indices().getIndex(getIndexRequest).actionGet();
			System.out.println(indexResponse.getMappings().toString());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}
    
    @Override
    public List<JSONObject> queryOfMatch(String indexname, String typename, Map<String, String> prarms, int page, int size) {
    	List<JSONObject> list = new ArrayList<>();
        try {
            TransportClient client = elasticsearchClientRepository.getObject();
            SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexname).setTypes(typename).setSize(size).setFrom(size*page);
            // System.out.println(indexResponse.getMappings().toString());
            //构建查询,它会将要查询的短语进行分词的
            this.generateMatchQueryBuilder(searchRequestBuilder, prarms, true);
            SearchResponse response =  searchRequestBuilder.execute().actionGet();
            SearchHits searchHits = response.getHits();
            Iterator<SearchHit> iterator = searchHits.iterator();
            while(iterator.hasNext()){
                SearchHit searchHit =  iterator.next();
                //Map<String, Object> map = searchHit.getSourceAsMap();
                String jsonStr = searchHit.getSourceAsString();
                JSONObject jsonObject = JSON.parseObject(jsonStr);
                list.add(jsonObject);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
	public int batchCreate(List<Map<String,Object>> list,String indexname,String typename) {
        try {

            TransportClient client = elasticsearchClientRepository.getObject();
            BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
            for(Map<String,Object> map : list){
                bulkRequestBuilder.add(client.prepareIndex(indexname,typename,uuidgenerator.generate().toString()).setSource(map));
            }
          BulkResponse bulkResponse = bulkRequestBuilder.execute().actionGet();
            int status = bulkResponse.status().getStatus();

            return status == 200 ? 200 : 500;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
	}

	@Override
	public List<Map<String, Object>> queryOfmatchPhrase(String indexname, String typename, Map<String, String> prarms, int page, int size) {
		List<Map<String, Object>> list = Lists.newArrayList();
        try {
            TransportClient client = elasticsearchClientRepository.getObject();
            SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexname).setTypes(typename).setSize(size).setFrom(size*page);
            // System.out.println(indexResponse.getMappings().toString());
            //构建查询，而它不会进行分词的
            this.generateMatchQueryBuilder(searchRequestBuilder, prarms,false);
            SearchResponse response =  searchRequestBuilder.execute().actionGet();
            SearchHits searchHits = response.getHits();
            Iterator<SearchHit> iterator = searchHits.iterator();
            while(iterator.hasNext()){
                SearchHit searchHit =  iterator.next();
                Map<String, Object> map = searchHit.getSourceAsMap();
                //精准查询附带上id
                map.put("id", searchHit.getId());
                list.add(map);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
	}
	
	/*
	 * 封装根据map添加查询字段组
	 * isMatchQuery true分词处理  false 全量匹配
	*/
	private void generateMatchQueryBuilder(SearchRequestBuilder searchRequestBuilder, Map<String, String> prarms,boolean isMatchQuery) throws RuntimeException{
		//没有查询字段 抛异常
		if(prarms==null || prarms.isEmpty()) {
			throw (new RuntimeException());
		}
		Set<String> keySet = prarms.keySet();
		Iterator<String> itertor = keySet.iterator();
		while(itertor.hasNext()) {
			String thisKey = itertor.next();
			if(isMatchQuery) {
				MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(thisKey,prarms.get(thisKey));
				searchRequestBuilder.setQuery(matchQueryBuilder);
			}else {
				//全搜索 不做分词匹配
				MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(thisKey,prarms.get(thisKey));
				searchRequestBuilder.setQuery(matchPhraseQueryBuilder);
			}
			
			
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> int batch(List<T> list, String indexname, String typename) {
		try {
			
            TransportClient client = elasticsearchClientRepository.getObject();
            BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
            for(T t : list){
            	
            	//注解的属性和javabean的属性有可能不一样的场景，//建立 注解和字段对于关系
                Map<String,String> field_ann_map = BeanPropertyResolve.builderFileAndAnnontationMpper(t);
                Map<String,Object> map = (Map<String,Object>)JsonUtil.jsonToObject(t.toString(), Map.class);
                Map<String,Object> transform = Maps.transformValues(field_ann_map, new  Function<String,Object>(){

    				@Override
    				public Object apply(String input) {
    					Object value = map.get(input);
    					return value;
    				}
                	
                });
                bulkRequestBuilder.add(client.prepareIndex(indexname,typename,uuidgenerator.generate().toString()).setSource(transform));
            }
            BulkResponse bulkResponse = bulkRequestBuilder.execute().actionGet();
            int status = bulkResponse.status().getStatus();
            return status == 200 ? 200 : 500;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
	}
	

	@Override
	public int batchAddField(String indexname, String typename,List<Map<String,String>> list) {
		try {

            TransportClient client = elasticsearchClientRepository.getObject();
            for(Map<String,String> t : list){
            	//构建更新请求对象
            	UpdateRequest updateRequest = new UpdateRequest(indexname, typename, t.get(elastic_object_id_key));
            	XContentBuilder source = XContentFactory.jsonBuilder().startObject();
            	//删除id，遍历key,组装需要添加的字段
            	t.remove(elastic_object_id_key);
            	Iterator<String> it = t.keySet().iterator();
            	while(it.hasNext()) {
            		String keyName = it.next();
            		String value = t.get(keyName);
            		source.field(keyName, value);
            	}
            	source.endObject();
            	updateRequest.doc(source);
            	client.update(updateRequest).actionGet().status().getStatus();
            }
            return 200;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
	}
	
	
}
